//==============================================================================
//File:	CGame.cpp
//Desc: This is the main demo/game application class.  This is where all the
//		application processing will take place.  The grand central station.
//	
//	I will be commenting the hell out of every file and
//	every line.  We can build after each lesson and delete what conflicts.  
//	This is the working project.
//
//Copyright this shit even if its just a demo.  Asad Aslam, Aaron Raines.
//==============================================================================

//==============================================================================
//CGame includes
//==============================================================================
#include "CGame.h"

//==============================================================================
//CGame Member Functions
//==============================================================================

//==============================================================================
//Name: CGame() aka CONSTRUCTOR
//Desc:	CGame Class Constructor
//==============================================================================
CGame::CGame()
{
	//Clear values
	m_hWnd			=NULL;
	m_pD3D			=NULL;
	m_pD3DDevice	=NULL;
	m_bLostDevice	=false;

}

//==============================================================================
//Name: ~CGame() aka DESTRUCTOR
//Desc:	CGame() Class Destructor
//=============================================================================
CGame::~CGame()
{
	//Shut down the engine
	ShutDown();
}

//========================================================================================
//File:	InitInstance()
//Desc:	Initialize the engine here.  All of it.  1st function called in WinMain. 
//When its done it returns control back to WinMain
//========================================================================================
bool CGame::InitInstance(HANDLE hInstance,LPCTSTR lpCmdLine, int iCmdShow)
{
	//Create the primary display device
	if(!CreateDisplay()) { Shutdown(); return false; }

	//Build the objects (2 boxes in this case)
	if(!BuildObjects()) {Shutdown(); return false; }

	//Set up all required game states
	SetupGameState();

	//Setup our rendering environment
	SetupRenderStates();

	//Yay it worked!
	return true;
}

//==============================================================================
//Name:	CreateDisplay() PRIVATE
//Desc:	Create the display windows, devices, rendering check. 1st function called
//		by InitInstance()
//==============================================================================
bool CGame::CreateDisplay()
{
	LPTSTR	WindowTitle	= _T("Initialization");
	USHORT	Width		= 400;
	USHORT	Height		= 400;
	RECT	rc;

	//Register the new windows window class.
	WNDCLASS			wc;
	wc.style			= CS_BYTEALIGNCLIENT | CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc		= StaticWndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hInstance		= (HINSTANCE)GetModuleHandle(NULL);
	wc.hIcon			= LoadIcon(wc.hInstance,MAKEINRESOURCE(IDI_ICON);
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground	= (HBRUSH) GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName		= NULL;
	wc.lpszClassName	= WindowTitle;
	RegisterClass(&wc);

	//Create the rendering window
	m_hWnd = CreateWindow(WindowTitle, WindowTitle, WS_OVERLAPPINGWINDOW, CW_USEDEFAULT,
							CW_USEDEFAULT, Width, Height, NULL, LoadMenu(wc.hInstance, MAKEINTRESOURCE(IDR_MENU)
							wc.hInstance, this);

	//Error! GET OUT!
	if(!m_hWnd) return false;

	//Retrieve the final client size of the window
	::GetClientRect(m_hWnd, &rc);
	m_nViewX			=rc.left;
	m_nViewY			=rc.top;
	m_nViewWidth		=rc.right - rc.left;
	m_nViewHeight		=rc.bottom - rc.top;

	//Show the window
	ShowWindow(m_hWnd, SW_SHOW);

	/*Initialize Direct3D
	IMPORTANT! Initialize after application window because we need the window handle
	for the Direct3DDevice9 object*/
	if (!InitDirect3D()) return false;

	//Success yay!!
	return true;

}

//=====================================================================================
//Name: InitDirect3D() - PRIVATE
//Desc: Non-enumerated Direct3D initialization
//=====================================================================================
bool CGame::InitDirect3D()
{
	HRESULT					hRet;
	D3DPRESENT_PARAMTERS	PresentParams;
	D3DCAPS9				Caps; //contains functionality and capablilites of device type on adapter. VERY LARGE structure
	D3DDISPLAYMODe			CurrentMode;

	//First create our D3D Object.  m_pD3D variable of the class.
	m_pD3D = Direct3DCreate9(D3D_SDK_VERSION); //D3D_SDK_VERSION is a var shipped with d3d9.h ensures correct init
	if(!m_pD3D)
	{
		MessageBox(m_hWnd, _T("No compatible Direct3D object could be created."), _T("Fatal Error!"), MB_OK | MB_ICONSTOP | MB_APPLMODAL);
		return false;
	}//End if something is wrong. No DirectX9 installed etc.

	//Fill out simple set of present parameters to be passed to CreateDevice().
	//First zero out structure to be safe
	ZeroMemory(&PresentParams, sizeof(D3DPRESENT_PARAMTERS));

	//Back buffer format selection.
	m_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &CurrentMode); //fill CurrentMode structure with primary adapater display mode data
	PresentParams.BackBufferFormat = CurrentMode.Format; //inform device we want frame buffer with matching pixel format of display mode

	//Setup all remaining flags
	PresentParams.AutoDepthStencil			= true; //depth buffer attached to frame buffer at device creation
	PresentParams.AutoDepthStencilFormat	= FindDepthStencilFormat(D3DADAPTER_DEFAULT, CurrentMode, D3DDEVTYPE_HAL); //FindDepthStencilFormat() is our function
	PresentParams.SwapEffect			 = D3DSWAPEFFECT_DISCARD; //allow device to choose presentation approach
	PresentParams.Windowed				 = true; //run in windowed mode

	//Set creation flags (may take a bit of explaining this is where dx10 kicks butt...grab some coffee)
	unsigned long ulFlags	= D3DCREATE_SOFTWARE_VERTEXPROCESSING; //ulFlags is set to software vertex processing (slow)
	
	//Check if Hardware T&L is available
	ZeroMemory(&Caps, sizeof(D3DCAPS9)); //zero out structure. its a very very large structure with lots of info
	//ask for capabilites of device type on adapter.  As we get more complicated we will access more member variables
	m_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &Caps);
	
	/*if HAL device supports T&L then DevCaps member of Caps will contain D3DDEVCAPS_HWTRANSFORMANDLIGHT
	if it does we want to use hardware so switch ulFlags to reflect this capability (fast)
	with today's adapters this should be no problem*/
	if(Caps.DevCaps & D3DDEVCAPS_HWTRANFORMANDLIGHT) ulFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
	
	//Now lets create the HAL device
	//If we can't then the person has a shit card and we probably don't want them to play our game anyway.
	if(FAILED (hRet = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd, ulFlags,
											&PresentParams, &m_pD3DDevice) ) )
	{
		MessageBox( m_hWnd, _T("Could not create a valid HAL Direct3D device object.\r\n\r\n")
                            _T("The system will now attempt to create a device utilising the 'Reference Rasterizer' (D3DDEVTYPE_REF)"),
                            _T("Fatal Error! Please join the 21st Century and get a better card!!"), MB_OK | MB_ICONINFORMATION | MB_APPLMODAL );

		//if in this loop then HAL creation failed.  Now we attempt to create a HEL device.  Nobody should reach here anyway but
		//its necessary I guess.  HEL device = reference rasterizer.  Basically same as above process but with HEL flags.

		//Find REF depth buffer format
		PresentParams.AutoDepthStencilFormat = FindDepthStencilFormat(D3D_ADAPTER_DEFAULT, CurrentMode, D3DDEVTYPE_REF);

		//check if hardware T&L is available
		ZeroMemory(&Caps, sizeof(D3DCAPS9));
		ulFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
		m_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, &Caps);
		if(Caps.DevCaps & D3DDEVCAPS_HWTRANFORMANDLIGHT) ulFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;

		//Attempt to actually create the REF device
		if( FAILED( hRet = m_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, m_hWnd, ulFlags, &PresentParams, &m_pD3DDevice ) ) ) 
        {
            MessageBox( m_hWnd, _T("Could not create a valid REF Direct3D device object.\r\n\r\nThe system will now exit."),
                                _T("Fatal Error! Something is really wrong with you! EPIC FAIL!"), MB_OK | MB_ICONSTOP | MB_APPLMODAL );

			//if this fails then the person doesnt actually have a computer.  They are attemptint to play the game on a
			//cardboard box with sticks and stones.

			//Epic Fail
			return false
		}//End if REF failure
	}//End if HAL failure

	//Store the present paramaters. variable of class
	m_D3DPresentParams = PresentParams;

	//Yay for success and people with actual graphic cards
	return true;
}//end of InitDirect3D()

//-----------------------------------------------------------------------------
// Name : FindDepthStencilFormat ()
// Desc : This function simply determines the best depth format that is
//        available for the specified mode.
// Note : No tests for stencil active depth buffers are made.
//-----------------------------------------------------------------------------
D3DFORMAT CGameApp::FindDepthStencilFormat( ULONG AdapterOrdinal, D3DDISPLAYMODE Mode, D3DDEVTYPE DevType )
{

    // Test for 32 bit depth buffer
    if (SUCCEEDED( m_pD3D->CheckDeviceFormat(AdapterOrdinal, DevType, Mode.Format, D3DUSAGE_DEPTHSTENCIL , D3DRTYPE_SURFACE , D3DFMT_D32 )))
    {
        if (SUCCEEDED( m_pD3D->CheckDepthStencilMatch ( AdapterOrdinal, DevType, Mode.Format, Mode.Format, D3DFMT_D32 ))) return D3DFMT_D32;
    
    } // End if 32bpp Available

    // Test for 24 bit depth buffer
    if (SUCCEEDED( m_pD3D->CheckDeviceFormat(AdapterOrdinal, DevType, Mode.Format, D3DUSAGE_DEPTHSTENCIL , D3DRTYPE_SURFACE , D3DFMT_D24X8 )))
    {
        if (SUCCEEDED( m_pD3D->CheckDepthStencilMatch ( AdapterOrdinal, DevType, Mode.Format, Mode.Format, D3DFMT_D24X8 ))) return D3DFMT_D24X8;
    
    } // End if 24bpp Available

    // Test for 16 bit depth buffer
    if (SUCCEEDED( m_pD3D->CheckDeviceFormat(AdapterOrdinal, DevType, Mode.Format, D3DUSAGE_DEPTHSTENCIL , D3DRTYPE_SURFACE , D3DFMT_D16 )))
    {
        if (SUCCEEDED( m_pD3D->CheckDepthStencilMatch ( AdapterOrdinal, DevType, Mode.Format, Mode.Format, D3DFMT_D16 ))) return D3DFMT_D16;
    
    } // End if 16bpp Available

    // No depth buffer supported
    return D3DFMT_UNKNOWN;

}
//-----------------------------------------------------------------------------
// Name : BuildObjects ()
// Desc : Demo Cube. Basically same as 1.1. 2nd function called by InitInstance
//-----------------------------------------------------------------------------
bool CGameApp::BuildObjects()
{
    CPolygon * pPoly = NULL;

    // Seed the random number generator
    srand( timeGetTime() );

    // Add 6 polygons to this mesh.
    if ( m_Mesh.AddPolygon( 6 ) < 0 ) return false;

    // Front Face
    pPoly = m_Mesh.m_pPolygon[0];
    if ( pPoly->AddVertex( 4 ) < 0 ) return false;
    
    pPoly->m_pVertex[0] = CVertex( -2,  2, -2, RANDOM_COLOR );
    pPoly->m_pVertex[1] = CVertex(  2,  2, -2, RANDOM_COLOR );
    pPoly->m_pVertex[2] = CVertex(  2, -2, -2, RANDOM_COLOR );
    pPoly->m_pVertex[3] = CVertex( -2, -2, -2, RANDOM_COLOR );
    
    // Top Face
    pPoly = m_Mesh.m_pPolygon[1];
    if ( pPoly->AddVertex( 4 ) < 0 ) return false;
    
    pPoly->m_pVertex[0] = CVertex( -2,  2,  2, RANDOM_COLOR );
    pPoly->m_pVertex[1] = CVertex(  2,  2,  2, RANDOM_COLOR );
    pPoly->m_pVertex[2] = CVertex(  2,  2, -2, RANDOM_COLOR );
    pPoly->m_pVertex[3] = CVertex( -2,  2, -2, RANDOM_COLOR );

    // Back Face
    pPoly = m_Mesh.m_pPolygon[2];
    if ( pPoly->AddVertex( 4 ) < 0 ) return false;

    pPoly->m_pVertex[0] = CVertex( -2, -2,  2, RANDOM_COLOR );
    pPoly->m_pVertex[1] = CVertex(  2, -2,  2, RANDOM_COLOR );
    pPoly->m_pVertex[2] = CVertex(  2,  2,  2, RANDOM_COLOR );
    pPoly->m_pVertex[3] = CVertex( -2,  2,  2, RANDOM_COLOR );

    // Bottom Face
    pPoly = m_Mesh.m_pPolygon[3];
    if ( pPoly->AddVertex( 4 ) < 0 ) return false;

    pPoly->m_pVertex[0] = CVertex( -2, -2, -2, RANDOM_COLOR );
    pPoly->m_pVertex[1] = CVertex(  2, -2, -2, RANDOM_COLOR );
    pPoly->m_pVertex[2] = CVertex(  2, -2,  2, RANDOM_COLOR );
    pPoly->m_pVertex[3] = CVertex( -2, -2,  2, RANDOM_COLOR );

    // Left Face
    pPoly = m_Mesh.m_pPolygon[4];
    if ( pPoly->AddVertex( 4 ) < 0 ) return false;

    pPoly->m_pVertex[0] = CVertex( -2,  2,  2, RANDOM_COLOR );
    pPoly->m_pVertex[1] = CVertex( -2,  2, -2, RANDOM_COLOR );
    pPoly->m_pVertex[2] = CVertex( -2, -2, -2, RANDOM_COLOR );
    pPoly->m_pVertex[3] = CVertex( -2, -2,  2, RANDOM_COLOR );

    // Right Face
    pPoly = m_Mesh.m_pPolygon[5];
    if ( pPoly->AddVertex( 4 ) < 0 ) return false;

    pPoly->m_pVertex[0] = CVertex(  2,  2, -2, RANDOM_COLOR );
    pPoly->m_pVertex[1] = CVertex(  2,  2,  2, RANDOM_COLOR ); 
    pPoly->m_pVertex[2] = CVertex(  2, -2,  2, RANDOM_COLOR );
    pPoly->m_pVertex[3] = CVertex(  2, -2, -2, RANDOM_COLOR );

    // Our two objects should reference this mesh
    m_pObject[ 0 ].m_pMesh = &m_Mesh;
    m_pObject[ 1 ].m_pMesh = &m_Mesh;

    // Set both objects matrices so that they are offset slightly
    D3DXMatrixTranslation( &m_pObject[ 0 ].m_worldMatrix, -3.5f,  2.0f, 14.0f );
    D3DXMatrixTranslation( &m_pObject[ 1 ].m_worldMatrix,  3.5f, -2.0f, 14.0f );
    
    // Success!
    return true;
}

//================================================================================
//Name:	SetupGameState()
//Desc:	Initial states required by game. 3rd function called by InitInstance()
//===============================================================================
void CGame::SetupGameState()
{
	//Setup default matrix values
	D3DXMatrixIdentity(&m_viewMatrix);

	//Rotation enabled
	m_bRotation1 = true;
	m_bRotation2 = true;

	//The application is active
	m_bActive = true;
}

//===============================================================================
//Name:	SetupRenderStates()
//Desc:	Initial states required by renderer. 4th and last function called by
//		InitInstance()
//===============================================================================
void CGame::SetupRenderStates()
{
	//Set up a perspective projection matrix. (Screen Transformation)
	float fAspect = (float)m_nViewWidth / (float)m_nViewHeight; //Aspect Ratio
	//Perspective Matrix creation. (matrix to hold, fov, aspect ratio, near plane, far plane)
	D3DXMatrixPerspectiveFovLH(&m_projectionMatrix, D3DXToRadian(60.0f), fAspect, 1.01f, 1000.0f);

	//Setup our D3D Device initial states
	m_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);	//use Z-Buffer for per-pixel depth test
	m_pD3DDevice->SetRenderState(D3DRS_DITHERENABLE, TRUE);		//enable dithering 16bit color combo 
	m_pD3DDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); //gouraud shading
	m_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);	//backface culling clockwise winding order
	m_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);		//no lighting

	//Setup our vertex FVF code
	m_pD3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE);

	//Setup our view and projection matrices
	m_pD3DDevice->SetTransform(D3DTS_VIEW, &m_viewMatrix);
	m_pD3DDevice->SetTransform(D3DTS_PROJECTION, &m_projectionMatrix);
}

//====================================================================================
//Name:	